/**
 * @file       Panel.cpp
 * @brief      TODO
 * @author     Huabo Zheng
 * @date       Jan 12, 2012
 * @Copyright  2012, www.netgear.com Inc. All rights reserved.
 */

#include "Panel.h"
#include "errorcode.h"
#include <algorithm>
#include "typedefine.h"
#include "Context.h"
#include "PagingStrategy.h"
#include "PagingByPanelStrategy.h"
#include "PageInfo.h"
#include "Point.h"
#include "PagingByObjectStrategy.h"
#include <sstream>
#include "AppEventListener.h"
#include "ClosePanelEvent.h"

namespace NS_LCD_AFW
{

CPanel::CPanel() :
		m_pFocusFrame(NULL), m_pPagingStrategy(NULL)
{
	m_framePtrArray.clear();
	this->setFocusable(true);
	this->setHightlightable(false);
	OBJ_NEW(CPagingByObjectStrategy, m_pPagingStrategy);
	this->m_pageInfo.setTotalPage(1);
	this->m_pageInfo.setCurPage(1);
	this->m_pageInfo.setDirection(CPageInfo::UNKNOW);
}

CPanel::~CPanel()
{
	this->cleanChildObject();
	m_pFocusFrame = NULL;
	OBJ_DELETE(m_pPagingStrategy);
	AfwGetContext()->remove(this);
}

CFrame *CPanel::getFocusObject() const
{
	return m_pFocusFrame;
}

CFrame *CPanel::setFocusObject(CFrame *pFrame)
{
	CFrame *pRet = m_pFocusFrame;
	m_pFocusFrame = pFrame;
	return pRet;
}

int CPanel::onCreate()
{
	//TODO: invoke before AfwGetContext()->add(this) will register this panel to the last context,doesn't it cause a problem?

	AfwGetContext()->add(this);
	CAppEventListener *pAppListener = new CAppEventListener(this, true);
	if (NULL != pAppListener)
	{
		this->addEventListener(pAppListener);
	}
	this->setFocus(true);
	return CFrame::onCreate();
}

int CPanel::add(CFrame *pFrame)
{
	if (NULL == pFrame)
	{
		return ERR_NULL_POINTER;
	}
	CFramePtrArray::iterator iter = std::find(m_framePtrArray.begin(),
			m_framePtrArray.end(), pFrame);
	if (iter != m_framePtrArray.end())
	{
		return ERR_ELEMENT_EXISTS;
	}
	CRect curRect = pFrame->getRect();
	int scale = curRect.getTop() / this->getRect().getHight() + 1;
	if (curRect.getHight() + curRect.getTop()
			> this->getRect().getHight() * scale)
	{
		curRect.setHight(this->getRect().getHight() * scale - curRect.getTop());

	}
	scale = curRect.getLeft() / this->getRect().getWidth() + 1;
	if (curRect.getWidth() + curRect.getLeft()
			> this->getRect().getWidth() * scale)
	{
		curRect.setWidth(
				this->getRect().getWidth() * scale - curRect.getLeft());
	}
	pFrame->setRect(curRect);
	CPoint pt = curRect.getLeftTop();
	if (curRect.getLeft() >= this->getRect().getWidth())
	{
		pt.setX(curRect.getLeft() % this->getRect().getWidth());
	}

	if (curRect.getTop() >= this->getRect().getHight())
	{
		pt.setY(curRect.getTop() % this->getRect().getHight());
	}

	pFrame->setPosition(pt + this->getPosition());
	pFrame->setVisible(false);
	m_framePtrArray.push_back(pFrame);
	calculatePaging();
	return ERR_SUCCESS;
}

int CPanel::remove(CFrame *pFrame)
{
	CFramePtrArray::iterator iter = std::find(m_framePtrArray.begin(),
			m_framePtrArray.end(), pFrame);
	if (iter != m_framePtrArray.end())
	{
		m_framePtrArray.erase(iter);

		calculatePaging();

		return ERR_SUCCESS;
	}
	return ERR_ELEMENT_NOT_FOUND;
}

CFrame *CPanel::getChild(const std::string & strName)
{
	CFramePtrArray::const_iterator iter = m_framePtrArray.begin();
	for (; iter != m_framePtrArray.end(); ++iter)
	{
		if ((*iter)->getName() == strName)
		{
			return (*iter);
		}
	}

	return NULL;
}

void CPanel::cleanChildObject()
{
	CFramePtrArray::iterator iter = m_framePtrArray.begin();
	for (; iter != m_framePtrArray.end(); ++iter)
	{
		//OBJ_DELETE(*iter);
		(*iter)->destory();
		(*iter) = NULL;
	}
	m_framePtrArray.clear();
	m_pFocusFrame = NULL;
}

int CPanel::calculateViewPort()
{
	this->onInvisible();
	int iRet = m_pPagingStrategy->calculateViewPort(this->m_framePtrArray,
			this->getRect(), m_pageInfo);
	this->onVisible();
	return iRet;
}

int CPanel::calculatePaging()
{
	std::sort(m_framePtrArray.begin(), m_framePtrArray.end(), sortFrameArray);
	return this->m_pPagingStrategy->calculatePaging(this->m_framePtrArray,
			this->getRect(), m_pageInfo);
}

void CPanel::close(int exitCode)
{
	CClosePanelEvent *pEvent = new CClosePanelEvent();
	pEvent->setParam(exitCode);
	this->postEvent(pEvent);
}

bool CPanel::sortFrameArray(CFrame *p1, CFrame *p2)
{
	return p1->getRect().getRightBottom().getY()
			< p2->getRect().getRightBottom().getY();
}

int CPanel::onDraw(IDisplay *pDisplay)
{
	int iRet = ERR_SUCCESS;
	if (ERR_SUCCESS != (iRet = CFrame::onDraw(pDisplay)))
	{
		return iRet;
	}

	if ((int) m_framePtrArray.size() > m_pageInfo.getHightLightObjIndex())
	{
		m_framePtrArray[m_pageInfo.getHightLightObjIndex()]->setHightlight(
				false);
	}
	calculateViewPort();
	if (this->m_pageInfo.isNeedSwitchPage())
	{
		CRect panelRect = this->getRect();
		pDisplay->cleanScreen(this->getPosition().getX(),
				this->getPosition().getY(), panelRect.getWidth(),
				panelRect.getHight());
		for (int i = m_pageInfo.getStartIndex(); i < m_pageInfo.getEndIndex();
				++i)
		{
			m_framePtrArray[i]->onDraw(pDisplay);
		}
		std::ostringstream strPageInfo;
		strPageInfo << m_pageInfo.getCurPage() << "/"
				<< m_pageInfo.getTotalPage();
//		strPageInfo.append("%d/%d", m_pageInfo.getCurPage(),
//				m_pageInfo.getTotalPage());
		int x = this->getRect().getRightBottom().getX()
				- strPageInfo.str().length() + this->getPosition().getX();
		int y = this->getRect().getRightBottom().getY()
				+ this->getPosition().getY();
		pDisplay->write(x, y, strPageInfo.str().length(), 1,
				strPageInfo.str().c_str());
	}

	if ((int) m_framePtrArray.size() > m_pageInfo.getHightLightObjIndex())
	{
		m_framePtrArray[m_pageInfo.getHightLightObjIndex()]->setHightlight(
				true);
	}
	return ERR_SUCCESS;
}

void CPanel::preCreate()
{
}

void CPanel::onSetFocus()
{
}

void CPanel::onLostFocus()
{
}

void CPanel::onHightlight()
{
}

void CPanel::onUnHightlight()
{
}

void CPanel::onSize(const CRect &oldRect, const CRect &newRect)
{
	calculatePaging();
}

int CPanel::onRightKeyPress()
{
	return CFrame::onRightKeyPress();
}

int CPanel::onLeftKeyPress()
{
	return CFrame::onLeftKeyPress();
}

int CPanel::onUpKeyPress()
{
	this->m_pageInfo.setDirection(CPageInfo::UP);
	return this->invalidate();
}

int CPanel::onDownKeyPress()
{
	this->m_pageInfo.setDirection(CPageInfo::DOWN);
	return this->invalidate();
}

int CPanel::onEnterKeyPress()
{
	for (int i = m_pageInfo.getStartIndex(); i < m_pageInfo.getEndIndex(); ++i)
	{
		if (m_framePtrArray[i]->isHightlight())
		{
			return m_framePtrArray[i]->setFocus(true);
		}
	}

	return CFrame::onEnterKeyPress();
}

int CPanel::onEscKeyPress()
{
	this->close();
	return ERR_SUCCESS;
}


int CPanel::handler(IEvent *pEvent)
{
	CFrame *pFrameHandler = getFocusObject();
	if (NULL == pFrameHandler)
	{
		//Task must have an object at all times be able to handle the event
		//return ERR_NO_FOCUS_OBJECT;
		return CFrame::notify(pEvent);
	}
	while (this != pFrameHandler)
	{
		if (ERR_SUCCESS == pFrameHandler->notify(pEvent))
		{
			return ERR_SUCCESS;
		}
		else
		{
			pFrameHandler = pFrameHandler->getParent();
		}

		if (NULL != pFrameHandler && pFrameHandler->isVisible())
		{
			pFrameHandler->setFocus(true);
		}
		else
		{
			break;
		}
	}
	return CFrame::notify(pEvent);
}

const CFramePtrArray & CPanel::getChildFrameArray() const
{
	return this->m_framePtrArray;
}

void CPanel::setPagingStrategy(CPagingStrategy *pStrategy)
{
	if (NULL == pStrategy)
	{
		return;
	}
	if (NULL != this->m_pPagingStrategy)
	{
		OBJ_DELETE(m_pPagingStrategy);
	}
	m_pPagingStrategy = pStrategy;
}

CPageInfo & CPanel::getPageInfo()
{
	return this->m_pageInfo;
}

void CPanel::preShow()
{
	this->m_pageInfo.setNeedSwitchPage(true);
	this->m_pageInfo.setDirection(CPageInfo::UNKNOW);
}

int CPanel::onClose()
{
	return ERR_SUCCESS;
}

void CPanel::onVisible()
{
	for (int i = m_pageInfo.getStartIndex();
			i < m_pageInfo.getEndIndex() && i < (int) m_framePtrArray.size();
			++i)
	{
		m_framePtrArray[i]->setVisible(true);
	}
}

void CPanel::onInvisible()
{
	for (int i = m_pageInfo.getStartIndex();
			i < m_pageInfo.getEndIndex() && i < (int) m_framePtrArray.size();
			++i)
	{
		m_framePtrArray[i]->setVisible(false);
	}
}

}

/* namespace NS_LCD_AFW */
